//===--- RValue.h - Exploded RValue Representation --------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// A storage structure for holding a destructured rvalue with an optional
/// cleanup(s).
///
/// Ownership of the rvalue can be "forwarded" to disable the associated
/// cleanup(s).
///
//===----------------------------------------------------------------------===//

#ifndef POLARPHP_PIL_GEN_RVALUE_H
#define POLARPHP_PIL_GEN_RVALUE_H

#include "polarphp/pil/gen/ManagedValue.h"
#include "polarphp/basic/NullablePtr.h"
#include "llvm/ADT/SmallVector.h"

namespace polar::lowering {

class ArgumentSource;
class Initialization;
class Scope;
class PILGenFunction;
class TypeLowering;

/// An "exploded" PIL rvalue, in which tuple values are recursively
/// destructured.
///
/// In terms of implementation, an RValue is a collection of ManagedValues that
/// the RValue class allows to be worked with as if they were one tuple. This
/// allows for tuples to represent tuples without needing to canonicalize into
/// the actual tuple value.
///
/// Once constructed, RValues obey the following invariants:
///
///   1. All non-trivially typed sub-ManagedValues must consistently have
///   cleanups. This is verified upon construction of an RValue.
///
///   2. All sub-ManagedValues with non-trivial ValueOwnershipKind must have the
///   same ValueOwnershipKind. There is a subtle thing occuring here. Since all
///   addresses are viewed from an ownership perspective as having trivial
///   ownership, this causes the verification to ignore address only
///   values. Once we transition to opaque values, the verification will
///   proceed.
///
///   3. All loadable sub-ManagedValues of an RValue must be of object
///   type. This means that if the lowered type of an RValue is loadable, then
///   the RValue's sub-parts must also be objects (i.e. not
///   addresses). Originally this was a hard invariant of RValue constructors,
///   but some parts of ArgEmission pass in addresses for loadable values. So
///   RValue loads them in the constructor.
///
///  FIXME(opaque_values): Update invariant #2 once address only types are no
///  longer emitted by PILGen.
///
/// *NOTE* In PILGen we don't try to explode structs, because doing so would
/// require considering resilience, a job we want to delegate to IRGen.
class RValue {
   friend class polar::lowering::Scope;
   friend class polar::lowering::ArgumentSource;

   std::vector<ManagedValue> values;
   CanType type;
   unsigned elementsToBeAdded;

   /// Flag value used to mark an rvalue as invalid.
   ///
   /// The reasons why this can be true is:
   ///
   /// 1. The RValue was consumed.
   /// 2. The RValue was default-initialized.
   /// 3. The RValue was emitted into an SGFContext initialization.
   enum : unsigned {
      Null = ~0U,
      Used = Null - 1,
      InContext = Used - 1,
   };

   bool isInSpecialState() const {
      return elementsToBeAdded >= InContext;
   }

   // Don't copy.
   RValue(const RValue &) = delete;
   RValue &operator=(const RValue &) = delete;

   void makeUsed() {
      elementsToBeAdded = Used;
      values = {};
   }

   /// Private constructor used by copy() and borrow().
   RValue(PILGenFunction &SGF, std::vector<ManagedValue> &&values, CanType type,
          unsigned elementsToBeAdded)
      : values(std::move(values)), type(type),
        elementsToBeAdded(elementsToBeAdded) {
      verify(SGF);
   }

   /// Private constructor for RValue::extractElement and pre-exploded element
   /// constructor.
   ///
   /// If SGF is nullptr, this constructor assumes that it is passed a
   /// pre-exploded set of ManagedValues that have already been verified as being
   /// RValue compatible since they once made up an RValue. If SGF is non-null,
   /// then we verify as well that all objects of loadable type are actually
   /// loaded (i.e. are objects).
   ///
   /// *NOTE* This constructor assumes that the constructed RValue is fully
   /// formed and thus has elementsToBeAdded set to zero.
   RValue(PILGenFunction *SGF, ArrayRef<ManagedValue> values, CanType type);

   RValue(unsigned state) : elementsToBeAdded(state) {
      assert(isInSpecialState());
   }

public:
   RValue() : elementsToBeAdded(Null) {}

   RValue(RValue &&rv) : values(std::move(rv.values)),
                         type(rv.type),
                         elementsToBeAdded(rv.elementsToBeAdded) {
      assert((rv.isComplete() || rv.isInSpecialState())
             && "moving rvalue that wasn't complete?!");
      rv.elementsToBeAdded = Used;
   }

   RValue &operator=(RValue &&rv) {
      assert((isNull() || isUsed()) && "reassigning an valid rvalue?!");

      assert((rv.isComplete() || rv.isInSpecialState())
             && "moving rvalue that wasn't complete?!");
      values = std::move(rv.values);
      type = rv.type;
      elementsToBeAdded = rv.elementsToBeAdded;
      rv.elementsToBeAdded = Used;
      return *this;
   }

   /// Create an RValue from a single value. If the value is of tuple type, it
   /// will be exploded.
   ///
   /// \param expr - the expression which yielded this r-value; its type
   ///   will become the substituted formal type of this r-value
   RValue(PILGenFunction &SGF, Expr *expr, ManagedValue v);

   /// Create an RValue from a single value. If the value is of tuple type, it
   /// will be exploded.
   RValue(PILGenFunction &SGF, PILLocation l, CanType type, ManagedValue v);

   /// Create a complete RValue from a pre-exploded set of elements.
   ///
   /// Since the RValue is assumed to be complete, no further values can be
   /// added.
   RValue(PILGenFunction &SGF, ArrayRef<ManagedValue> values, CanType type)
      : RValue(&SGF, values, type) {}

   /// Creates an invalid RValue object, in an "in-context" state.
   static RValue forInContext() {
      return RValue(InContext);
   }

   static unsigned getRValueSize(CanType substType);
   static unsigned getRValueSize(AbstractionPattern origType, CanType substType);

   /// Create an RValue to which values will be subsequently added using
   /// addElement(), with the level of tuple expansion in the input specified
   /// by the abstraction pattern. The RValue will not be complete until all
   /// the elements have been added.
   explicit RValue(AbstractionPattern pattern, CanType type);

   /// Create an RValue to which values will be subsequently added using
   /// addElement(). The RValue will not be complete until all the elements have
   /// been added.
   explicit RValue(CanType type);

   /// Return true if the rvalue was null-initialized. The intention is so one
   /// can trampoline RValue results using if statements, i.e.:
   ///
   /// if (RValue rv = foo()) {
   ///   return rv;
   /// }
   operator bool() const & { return isComplete() || isInContext(); }

   /// True if the rvalue has been completely initialized by adding all its
   /// elements.
   bool isComplete() const & { return elementsToBeAdded == 0; }

   /// True if the rvalue was null-initialized.
   bool isNull() const & { return elementsToBeAdded == Null; }

   /// True if this rvalue has been used.
   bool isUsed() const & { return elementsToBeAdded == Used; }

   /// True if this rvalue was emitted into context.
   bool isInContext() const & { return elementsToBeAdded == InContext; }

   /// Add an element to the rvalue. The rvalue must not yet be complete.
   void addElement(RValue &&element) &;

   /// Add a ManagedValue element to the rvalue, exploding tuples if necessary.
   /// The rvalue must not yet be complete.
   void addElement(PILGenFunction &SGF, ManagedValue element,
                   CanType formalType, PILLocation l) &;

   /// Forward an rvalue into a single value, imploding tuples if necessary.
   PILValue forwardAsSingleValue(PILGenFunction &SGF, PILLocation l) &&;

   /// Forward an rvalue into a single value, imploding tuples if necessary, and
   /// introducing a potential conversion from semantic type to storage type.
   PILValue forwardAsSingleStorageValue(PILGenFunction &SGF,
                                        PILType storageType,
                                        PILLocation l) &&;

   /// Get the rvalue as a single value, imploding tuples if necessary.
   ManagedValue getAsSingleValue(PILGenFunction &SGF, PILLocation l) &&;

   /// Get the rvalue as a single unmanaged value, imploding tuples if necessary.
   /// The values must not require any cleanups.
   PILValue getUnmanagedSingleValue(PILGenFunction &SGF, PILLocation l) const &;

   ManagedValue getScalarValue() && {
      assert(!isa<TupleType>(type) && "getScalarValue of a tuple rvalue");
      assert(values.size() == 1);
      auto value = values[0];
      makeUsed();
      return value;
   }

   /// Returns true if this is an rvalue that can be used safely as a +1 rvalue.
   ///
   /// This returns true iff:
   ///
   /// 1. All sub-values are trivially typed.
   /// 2. There exists at least one non-trivial typed sub-value and all such
   /// sub-values all have cleanups.
   ///
   /// *NOTE* Due to 1. isPlusOne and isPlusZero both return true for rvalues
   /// consisting of only trivial values.
   bool isPlusOne(PILGenFunction &SGF) const &;

   /// Returns true if this is an rvalue that can be used safely as a +0 rvalue.
   ///
   /// Specifically, we return true if:
   ///
   /// 1. All sub-values are trivially typed.
   /// 2. At least 1 subvalue is non-trivial and all such non-trivial values do
   /// not have a cleanup.
   ///
   /// *NOTE* Due to 1. isPlusOne and isPlusZero both return true for rvalues
   /// consisting of only trivial values.
   bool isPlusZero(PILGenFunction &SGF) const &;

   /// Use this rvalue to initialize an Initialization.
   void forwardInto(PILGenFunction &SGF, PILLocation loc, Initialization *I) &&;

   /// Copy this rvalue to initialize an Initialization without consuming the
   /// rvalue.
   void copyInto(PILGenFunction &SGF, PILLocation loc, Initialization *I) const&;

   /// Assign this r-value into the destination.
   void assignInto(PILGenFunction &SGF, PILLocation loc, PILValue destAddr) &&;

   /// Forward the exploded PILValues into a SmallVector.
   void forwardAll(PILGenFunction &SGF,
                   SmallVectorImpl<PILValue> &values) &&;

   ManagedValue materialize(PILGenFunction &SGF, PILLocation loc) &&;

   /// Take the ManagedValues from this RValue into a SmallVector.
   void getAll(SmallVectorImpl<ManagedValue> &values) &&;

   /// Store the unmanaged PILValues into a SmallVector. The values must not
   /// require any cleanups.
   void getAllUnmanaged(SmallVectorImpl<PILValue> &values) const &;

   /// Extract a single tuple element from the rvalue.
   RValue extractElement(unsigned element) &&;

   /// Extract the tuple elements from the rvalue.
   void extractElements(SmallVectorImpl<RValue> &elements) &&;

   CanType getType() const & { return type; }

   /// Return the lowered type associated with the given CanType's type lowering.
   PILType getLoweredType(PILGenFunction &SGF) const &;

   /// Return the type lowering of RValue::getType().
   const lowering::TypeLowering &getTypeLowering(PILGenFunction &SGF) const &;

   /// Return the lowered PILType that would be used to implode the given RValue
   /// into 1 tuple value.
   ///
   /// This means that if any sub-objects are address only, an address type will
   /// be returned. Otherwise, an object will be returned. So this is a
   /// convenient way to determine if an RValue needs an address.
   PILType getLoweredImplodedTupleType(PILGenFunction &SGF) const &;

   /// Emit an equivalent value with independent ownership.
   RValue copy(PILGenFunction &SGF, PILLocation loc) const &;

   /// If this RValue is a +0 value, copy the RValue and return. Otherwise,
   /// return std::move(*this);
   RValue ensurePlusOne(PILGenFunction &SGF, PILLocation loc) &&;

   /// Borrow all subvalues of the rvalue.
   RValue borrow(PILGenFunction &SGF, PILLocation loc) const &;

   RValue copyForDiagnostics() const;

   static bool areObviouslySameValue(PILValue lhs, PILValue rhs);
   bool isObviouslyEqual(const RValue &rhs) const;

   void dump() const;
   void dump(raw_ostream &OS, unsigned indent = 0) const;

   /// Verify RValue invariants.
   ///
   /// This checks ownership invariants and also checks that all sub managed
   /// values that are loadable are actually objects.
   ///
   /// *NOTE* This is a no-op in non-assert builds.
   void verify(PILGenFunction &SGF) const &;
};

} // end namespace polar::lowering

#endif // POLARPHP_PIL_GEN_RVALUE_H
